package com.sanri.tools.bigfileupload;


import com.sanri.tools.bigfileupload.dtos.FileMeta;
import com.sanri.tools.bigfileupload.dtos.SecondUploadReq;
import com.sanri.tools.bigfileupload.dtos.SecondUploadResp;
import com.sanri.tools.bigfileupload.entity.BigFileMeta;
import com.sanri.tools.bigfileupload.storage.FileStorage;
import com.sanri.tools.bigfileupload.storage.MetaManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;

@Service
@Slf4j
public class BigFileUploadService {
    @Autowired
    private MetaManager metaManager;

    @Autowired
    private FileStorage fileStorage;

    /**
     * 秒传逻辑
     * @param secondUploadReq
     */
    public SecondUploadResp secondUpload(SecondUploadReq secondUploadReq){
        BigFileMeta bigFileMeta = metaManager.selectOriginFile(secondUploadReq.getMd5());

        // 文件元对象都没有
        if (bigFileMeta == null ){
            bigFileMeta = new BigFileMeta();
            bigFileMeta.setOriginName(secondUploadReq.getOriginFileName());
            bigFileMeta.setLink("origin");
            bigFileMeta.setBucket(secondUploadReq.getBucket());
            bigFileMeta.setMd5(secondUploadReq.getMd5());
            bigFileMeta.setFilePath(calcFilePath(secondUploadReq));
            bigFileMeta.setPosition(0L);
            bigFileMeta.setFileSize(secondUploadReq.getFileSize());

            // 元数据入库
            metaManager.saveMeta(bigFileMeta);

            SecondUploadResp secondUploadResp = new SecondUploadResp(false,bigFileMeta);
            return secondUploadResp;
        }

        // 文件元对象有了, 但是没上传完成
        if (bigFileMeta.getPosition() < bigFileMeta.getFileSize()){
            return new SecondUploadResp(false, bigFileMeta);
        }

        // 如果当前文件进度 100% 可以秒传
        BigFileMeta copyFile = new BigFileMeta();
        BeanUtils.copyProperties(bigFileMeta, copyFile);
        copyFile.setId(null);
        copyFile.setLink(bigFileMeta.getId());
        copyFile.setOriginName(secondUploadReq.getOriginFileName());
        metaManager.saveMeta(copyFile);
        return new SecondUploadResp(true, copyFile);
    }

    /**
     * 提前设置文件路径或唯一id 值
     *
     * @param secondUploadReq
     * @return
     */
    private String calcFilePath(SecondUploadReq secondUploadReq) {
        String originFileName = secondUploadReq.getOriginFileName();

        String baseName = FilenameUtils.getBaseName(originFileName);
        String extension = FilenameUtils.getExtension(originFileName);

        String fileId = baseName + RandomStringUtils.randomNumeric(4) + "."+ extension;
        return fileId;
    }

    /**
     * 追加一段上传
     * @param md5
     * @param part
     */
    public void appendPart(String md5, byte[] part) throws IOException {
        BigFileMeta bigFileMeta = metaManager.selectOriginFile(md5);

        if (bigFileMeta == null){
            throw new RuntimeException("先检查文件是否可以秒传");
        }
        String bucket = bigFileMeta.getBucket();
        String filePath = bigFileMeta.getFilePath();

        fileStorage.appendPart(bucket, filePath, part);
    }

    /**
     * 上传文件检验
     * @param md5
     */
    public void validateFile(String md5) throws IOException {
        BigFileMeta bigFileMeta = metaManager.selectOriginFile(md5);

        if (bigFileMeta == null){
            throw new RuntimeException("先检查文件是否可以秒传");
        }

        final FileMeta fileMeta = fileStorage.fileMeta(bigFileMeta.getBucket(), bigFileMeta.getFilePath());
        final Long realSize = fileMeta.getFileSize();
        final Long expectFileSize = bigFileMeta.getFileSize();

        if (realSize.longValue() != expectFileSize.longValue()){
            log.error("大文件上传失败，文件大小不完整 "+realSize+" != "+expectFileSize);
            throw new RuntimeException("大文件上传失败，文件大小不完整 "+realSize+" != "+expectFileSize);
        }

        if (!fileMeta.getMd5().equals(bigFileMeta.getMd5())){
            log.error("大文件上传失败，文件损坏 "+fileMeta.getMd5()+" != "+md5);
            throw new RuntimeException("大文件上传失败，文件损坏 "+fileMeta.getMd5()+" != "+md5);
        }
    }

    public FileMeta filePosition(String md5) throws IOException {
        BigFileMeta bigFileMeta = metaManager.selectOriginFile(md5);

        if (bigFileMeta == null){
            throw new RuntimeException("先检查文件是否可以秒传");
        }

        FileMeta fileMeta = fileStorage.fileMeta(bigFileMeta.getBucket(), bigFileMeta.getFilePath());
        return fileMeta;
    }
}
